`ifndef _DEVICES_V
`define _DEVICES_V 1

`include "Defines.v"

module SevenSeg(OUT, IN);
	output[6:0] OUT;
	input [7:0] IN;
	assign OUT=
		(IN == 4'd0) ? 7'b1000000 :
		(IN == 4'd1) ? 7'b1111001 :
		(IN == 4'd2) ? 7'b0100100 :
		(IN == 4'd3) ? 7'b0110000 :
		(IN == 4'd4) ? 7'b0011001 :
		(IN == 4'd5) ? 7'b0010010 :
		(IN == 4'd6) ? 7'b0000010 :
		(IN == 4'd7) ? 7'b1111000 :
		(IN == 4'd8) ? 7'b0000000 :
		(IN == 4'd9) ? 7'b0010000 :
		(IN == 4'd10) ? 7'b0001000 :
		(IN == 4'd11) ? 7'b0000011 :
		(IN == 4'd12) ? 7'b1000110 :
		(IN == 4'd13) ? 7'b0100001 :
		(IN == 4'd14) ? 7'b0000110 :
		(IN == 4'd15) ? 7'b0001110 :
					    7'b0110000 ;
					    
endmodule

// Timer device
module Timer(ABUS,DBUS,WE,INTR,CLK,LOCK,INIT,DEBUG);
	parameter BITS;
	parameter BASE;
	parameter DIVN;
	parameter DIVB;
	input wire [(BITS-1):0] ABUS;
	inout wire [(BITS-1):0] DBUS;
	input wire WE,CLK,LOCK,INIT;
	output wire INTR;
	// This can be used for debugging, just connect the DEBUG
	// output from this device to LEDG :)
	// Nut note how it remains unconnected in the instantiation above!
	output wire [7:0] DEBUG={TCNT[13:10],OR,AR,IP,IE};
	// This is our TCTL, but I chose to keep it as separate bits
	reg OR,AR,IP,IE;
	// TINI and TCNT registers
	reg [(BITS-1):0] TINI,TCNT;
	// Does the address refer to our TCTL or TCNT/TINI registers?
	wire selCtl=(ABUS==BASE);
	wire selCnt=(ABUS==BASE+3'h4);
	// Write and read enable signals for each of these
	wire wrCtl=WE&&selCtl;
	wire wrCnt=WE&&selCnt;
	wire rdCtl=(!WE)&&selCtl;
	wire rdCnt=(!WE)&&selCnt;
	
	reg [(BITS-1):0] tmpCount;//=DIVN;
	reg counting; //used to only throw an interrupt after we start counting
	
	always @(posedge CLK) if(LOCK) begin
		if(INIT) begin //init the timer
			{IE,IP,AR,OR} <= {1'b0, 1'b0, 1'b0, 1'b0};
			TINI <= 32'd0;
			TCNT <= 32'd0;
			counting <= 1'b0;
			tmpCount<=DIVN;
		end else if(wrCtl) begin //write to the "Control" register
			//not sure about this
			//{OR,IE,IP,AR} <={DBUS[6:4],DBUS[7]};
			//OR,AR,IP,IE
			{IE,IP,AR,OR} <= {DBUS[0],DBUS[1],DBUS[2],DBUS[3]};
		end else if(wrCnt) begin //write to the "Count" register
			TCNT <= DBUS;
			TINI <=	DBUS;
		end	else if(TCNT > 1'b0) begin //if the timer is > 0
			counting <= 1'b1; //flag that we've been counting
			if(tmpCount == 0) begin
				TCNT<=TCNT-1'b1; //decrease by a second
				tmpCount <= DIVN; //resent freq count
			end else begin
				tmpCount <= tmpCount-1'd1; //decrease freq count
			end
		end else if(counting) begin //if the timer reaches 0 after counting
			if(IP) OR <= 1'b1; //if we're already pending an interrupt, set overrun to true
			if(IE) IP <= 1'b1; //if interrupts are enabled, flag an interrupt
			
			TCNT <= AR?TINI:{(BITS){1'b0}}; //reset COUNT if auto restart is set, else leave at 0.
			counting <= AR?1'b1:1'b0; //update counting based on AR
			//NOTE: This will throw an OR if we're not using AR, is this OK?
			
			//When AR is not set, if we write to TINI/TCNT the counter is supposed to count down every millisecond, request an 
			//interrupt when it
			// reaches zero, and then sit there waiting for another write to TINI/TCNT. When AR is one, when the counter 
			//reaches zero the timr shoudl request an interrupt and reload the counter from TINI and start counting again 
			//(and eventually interrupt the processor again and restart the count again, etc.).
		end
		else begin
			{IE,IP,AR,OR} <= {IE, IP, AR, OR};
			TINI <= TINI;
			TCNT <= TCNT;
			counting <= counting;
		end
	end

	// Drive the bus only if processor is reading one of the two addresses
	// this device is responsible for
	assign DBUS=rdCtl?{{(BITS-4){1'b0}},OR,AR,IP,IE}:
			  rdCnt?TCNT:{BITS{1'bz}};
	// Ask for interrupt only if interrupt pending (IP) and enabled (IE)
	assign INTR=(IP&&IE);
endmodule
// This is the display device

module Display(ABUS,DBUS,WE,CLK,LOCK,INIT,HEX0,HEX1,HEX2,HEX3);
  parameter BITS;
  parameter BASE;
  input wire [(BITS-1):0] ABUS;
  inout wire [(BITS-1):0] DBUS;
  input wire WE,CLK,LOCK,INIT;
  output wire [6:0] HEX0,HEX1,HEX2,HEX3;
  reg [15:0] HexVal;
  SevenSeg ss3(.OUT(HEX3),.IN(HexVal[15:12]));
  SevenSeg ss2(.OUT(HEX2),.IN(HexVal[11: 8]));
  SevenSeg ss1(.OUT(HEX1),.IN(HexVal[ 7: 4]));
  SevenSeg ss0(.OUT(HEX0),.IN(HexVal[ 3: 0]));
  wire selDisp=(ABUS==BASE);
  wire wrDisp=WE&&selDisp;
  always @(posedge CLK) if(LOCK) begin
    if(INIT)
      HexVal<=16'hDEAD;
    else if(wrDisp)
      HexVal<=DBUS[15:0];
  end
endmodule

module Leds(ABUS,DBUS,WE,CLK,LOCK,INIT,LED);
  parameter ABITS;
  parameter DBITS;
  parameter LBITS;
  parameter BASE;
  input wire [(ABITS-1):0] ABUS;
  inout wire [(DBITS-1):0] DBUS;
  input wire WE,CLK,LOCK,INIT;
  output wire [(LBITS-1):0] LED=val;
  reg [(LBITS-1):0] val;
  wire sel=(ABUS==BASE);
  wire wri=WE&&sel;
  always @(posedge CLK) if(LOCK) begin
    if(INIT)
      val<={LBITS{1'b0}};
    else if(wri)
      val<=DBUS[(LBITS-1):0];
  end
endmodule

// This is the complete key device
module KeyDev(ABUS,DBUS,WE,INTR,CLK,LOCK,INIT,KEY,DEBUG);
  parameter BITS;
  parameter BASE;
  input wire [(BITS-1):0] ABUS;
  inout wire [(BITS-1):0] DBUS;
  input wire WE,CLK,LOCK,INIT;
  input wire [3:0] KEY;
  output wire INTR;
  output wire [7:0] DEBUG={1'b0,OR,IE,IP,KEY};
  wire selKey=(ABUS==BASE);
  wire wrKey=WE&&selKey;
  wire rdKey=(!WE)&&selKey;
  reg [3:0] prev;
  reg IP,IE,OR;
  // What does IP_curr do? Well, what if a key is pressed in
  // a cycle then a WCTL instruction is writing to KCTL
  wire IP_curr=wrKey?DBUS[4]:IP;
  always @(posedge CLK) if(LOCK) begin
    if(INIT) begin
      prev<=KEY;
      {OR,IE,IP}<=3'b000;
    end else begin
      if(wrKey) begin
        {OR,IE,IP}<=DBUS[6:4];
      end
      if(prev!=KEY) begin
        if(IP_curr)
          OR<=1'b1;
        IP<=1'b1;
        prev<=KEY;
      end
    end    
  end
  assign DBUS=rdKey?{{(BITS-7){1'b0}},OR,IE,IP,KEY}:
                    {BITS{1'bz}};
  assign INTR=IP&&IE;
endmodule

module SwDev(ABUS,DBUS,WE,INTR,CLK,LOCK,INIT,SW,DEBUG);
  parameter BITS;
  parameter BASE;
  // How many cycles to count to get to 10ms
  parameter DEBN;
  // How many bits do we need for that count?
  parameter DEBB;
  inout wire [(BITS-1):0] ABUS;
  inout wire [(BITS-1):0] DBUS;
  input wire WE,CLK,LOCK,INIT;
  input [9:0] SW;
  output wire [9:0] DEBUG={switches[9:3],IP,IE,OR};
  output wire INTR;
  
  wire selSw=(ABUS==BASE);
  wire wrSw=WE&&selSw;
  wire rdSw=(!WE)&&selSw;
  reg [9:0] prev;
  reg [9:0] switches;
  reg IP,IE,OR;
  wire IP_curr=wrSw?DBUS[10]:IP;
  reg [DEBB-1:0] counter;
  reg counting;
  always @(posedge CLK) if(LOCK) begin
	if(INIT) begin
		{OR,IE,IP}<=3'b000;
		counter <=0;
		prev<=SW;
		switches<=SW;
		counting<=1'b0;
	end else begin
		if(wrSw) begin
			{OR,IE,IP}<=DBUS[12:10];
		end
		if ((prev != SW ) && !counting) begin
			prev <= SW;
			counting <=1'b1;
			counter <= 1'b0;
		end else if(counter == DEBN) begin
			//prev <= SW;
				if (prev==SW) begin
					switches <= prev;//SW;
					if(IP_curr)
						OR <= 1'b1;
					IP<= 1'b1;
					//counter <= (IP&&IE) ? 1'b0 : count;
					counter <= 1'b0;
					counting<= 1'b0;
				end
		end else if(counting) begin
			//prev <= SW;
			counter <= counter+1'b1;
		end
	end
  end
  assign DBUS=rdSw? {{(BITS-13){1'b0}},OR,IE,IP,switches} :
                    {BITS{1'bz}};
  assign INTR=IP&&IE;
endmodule


`endif
